home *** CD-ROM | disk | FTP | other *** search
- From: kanze@lts.sel.alcatel.de (James Kanze US/ESC 60/3/141 #40763)
- Message-ID: <KANZE.96Feb5132431@slsvewt.lts.sel.alcatel.de>
- X-Original-Date: 05 Feb 1996 12:24:28 GMT
- Path: in1.uu.net!bounce-back
- Date: 06 Feb 96 02:35:51 GMT
- Approved: fjh@cs.mu.oz.au
- Newsgroups: comp.std.c++
- Subject: Re: class.union questions
- In-Reply-To: dirk@becker.adviser.com's message of 03 Feb 96 17:22:11 GMT
- Organization: SEL
- References: <v01530500ad38ee830c96@[194.163.74.11]>
- Apparently-To: std-c++@ncar.ucar.edu
- X-Auth: PGPMoose V1.1 PGP comp.std.c++
- iQBFAgUBMRa+ouEDnX0m9pzZAQE6CAF9ENug9/g7C1pecakAJvzfFetiirJEg3Uv
- TwmlbBbghQgvuIAfGBInIk2kxgowu6Aj
- =yvyy
-
- In article <v01530500ad38ee830c96@[194.163.74.11]>
- dirk@becker.adviser.com (Dirk Becker) writes:
-
- |> In Section 9.6.1 Unions [class.union] there is a large collection of
- |> restrictions on the union itself and on candidate member classes.
- |> I would like to ask for reasons of several of them:
-
- One of the principle goals was that a union be compatible with a C
- union, at least in use. One of the rules is that the addresses of all
- of the elements in the union compare equal to the address of the union
- itself, when cast to the appropriate type.
-
- |> a) "A union shall not have base classes. A union shall not be used as
- |> a base class."
-
- Without this rule, you either break the address rule, or you impose
- some special class layout on unions.
-
- |> This prevents any construction like the following, where one would
- |> like to extend the union contained in some base class:
-
- |> class GenericInterpreter {
- |> union opcode {
- |> short tag;
- |> struct { short tag; short data; } op1;
- |> struct { short tag; char* data; } op2;
- |> };
- |> };
-
- |> class SpecialInterpreter : public GenericInterpreter {
- |> union opcode : public GenericInterpreter::opcode {
- |> struct { short tag; long data; } op3;
- |> struct { short tag; float data; } op4;
- |> };
- |> };
-
- This is generally handled better by an abstract base class and virtual
- functions.
-
- |> b) "An object of a class with a non-trivial default constructor
- |> (_class.ctor_), a non-trivial copy constructor (_class.copy_),
- |> a non-trivial destructor (_class.dtor_), or a non-trivial copy
- |> assignment operator (_over.ass_, _class.copy_) cannot be a member
- |> of a union,"
-
- |> This is a major reduction of the restrictions found in ARM, where all
- |> kinds of constructors or assignment operators were excluded. Here is
- |> also a note to give the explanation by the assumption, any member
- |> functions and especially assignments would usually expect a correctly
- |> constructed object.
-
- This is, in fact, exactly the restriction in the ARM. All that has
- changes is the way things are described. In the ARM, classes may or
- may not have constructors (destructors, assignment operators). In the
- current standard, *EVERY* class/struct has a copy constructor, an
- assignment operator and a destructor; the distinction is that some of
- these are trivial.
-
- A user declared constructor, destructor or assignment operator is by
- definition never trivial.
-
- |> The quoted WP sequence tries its best to exclude virtual function
- |> tables from the union members, which is just fine. But through the
- |> non-trivialness you also exclude _any_ custom make of the copy
- |> constructor and assignment operator.
- |> So you can have any special assignment operator or constructor for
- |> your member objects, any but the most common and useful ones.
-
- Correct. The reason is simple: assignment, copy, default construction
- and destruction of a union must be correctly defined without the
- compiler knowing which element the union actually contains. The set
- of forbidden operators are those that the compiler might be called
- upon to use in implicitly generated code.
-
- |> Another sample:
-
- |> class longlong { ... };
- |> struct opcode {
- |> short tag;
- |> union {
- |> short op1;
- |> long op2;
- |> longlong op3;
- |> } data;
- |> };
-
- |> If you are lucky to have some native long long datatype, then this
- |> would be legal code. If you already had to implement your own
- |> class longlong, now you lost the chance to use it (here).
-
- This is a problem, but... how would the compiler know when to call the
- constructor/destructor of your longlong?
-
- |> c) "A union can be thought of as a class"
-
- |> Here you accept the implications of 12.8.8 and 12.8.13 on implicitly
- |> defined copy constructor and copy assignment. The already trivial
- |> copy constructor/assignment of the member objects will then result
- |> in one large repetition of copies from and to the same memory locations.
- |> To avoid this behaviour I would strongly recommend to implement your
- |> own copy constructor or assignment whenever you use a union, because a
- |> memberwise copy is usually not desired.
-
- Basically, if you study the restrictions on unions carefully, the
- rules are such that a bitwise copy *must* be legal for assignment and
- copy construction. This is the only type of copy the compiler can
- cope with in the absense of a discriminator to tell it which element
- of the union is valid.
-
- IMHO: if you feel that your union needs a copy constructor or an
- assignment operator, you are probably doing something wrong, in the
- sense that you are trying to use unions in a way that they are not
- intended. (As they are defined in the standard. If they were defined
- differently, they might have different reasonable uses.)
-
- In practice, I don't think I've used a union in my code since
- switching to C++, except in memory management code (ensuring
- alignment, etc.).
-
- |> Let's combine this with the problems of a) and b):
-
- |> class GenericInterpreter {
- |> union opcode {
- |> opcode(const opcode&); // replace implicitly defined
- |> opcode& operator = (const opcode&); // memberwise copy versions
-
- |> short tag;
- |> struct { short tag; short data; } op1;
- |> struct { short tag; char* data; } op2;
- |> };
- |> };
-
- |> class SpecialInterpreter : public GenericInterpreter {
- |> union opcode {
- |> GenericInterpreter::opcode generic; // error: non-trivial member
- |> short tag;
- |> struct { short tag; long data; } op3;
- |> struct { short tag; float data; } op4;
- |> };
- |> };
-
- |> class ValidInterpreter : public GenericInterpreter {
- |> union opcode {
- |> // using copy - paste instead of language features
- |> short tag;
- |> struct { short tag; short data; } op1;
- |> struct { short tag; char* data; } op2;
- |> //
- |> struct { short tag; long data; } op3;
- |> struct { short tag; float data; } op4;
- |> };
- |> };
-
- |> In my opinion, comparing the copy constructor/assignments to others, they
- |> are only special regarding their use by the implicitly defined memberwise
- |> copy constructor/assignment by outside classes. Here I can't find a reason
- |> why a member's custom copy methods should not be applicable instead.
-
- |> Conclusion, just dreaming:
- |> I would generally prefer " ... constructors and destructor of member
- |> objects are ignored, if not explicitly called by the union's ctor/dtor.
- |> The union's implicit default copy constructor / assignment does a
- |> binary copy instead of memberwise copy. Objects <containing a vptr>
- |> cannot be member of a union" - Please pardon my shortcut on vptrs.
-
- There was some discussion concerning this. There were two points of
- view: one supported basically what you are proposing, the other the
- current situation. The current situation won.
-
- I argued the current situation on the grounds that the above is just
- too dangerous. Basically, it makes it too simple to access
- unconstructed objects of types which require a constructor. Most of
- the uses I see for a union are at the low level, where C like code is
- perfectly appropriate.
-
- IMHO, things like your example are much better handled with derived
- classes (in which the vptr replaces the tag for type information).
- Even in cases where the union might be more appropriate, what would be
- needed is a discriminated union, which is a completely different beast
- than the union we now have. (I've yet to see what I would consider a
- convincing example of the advantage of a discriminated union over
- polymorphism, but I know that others disagree with me here.)
-
- |> d) anonymous union
-
- |> Why is there no anonymous struct? This is not only for symmetric reasons,
- |> but anonymous structs inside unions may be as useful as anonymous unions
- |> inside structs.
-
- Agreed here. Although I really don't see it as worth the bother. (I
- could do without anonymous unions, too.)
- --
- James Kanze Tel.: (+33) 88 14 49 00 email: kanze@gabi-soft.fr
- GABI Software, Sarl., 8 rue des Francs-Bourgeois, F-67000 Strasbourg, France
- Conseils, itudes et rialisations en logiciel orienti objet --
- -- A la recherche d'une activiti dans une region francophone
- ---
- [ comp.std.c++ is moderated. Submission address: std-c++@ncar.ucar.edu.
- Contact address: std-c++-request@ncar.ucar.edu. The moderation policy
- is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]
-